#include <stdio.h>
#include <stdlib.h>

#include "double_list.h"

typedef struct _dlist_node_ {
	struct _dlist_node_  * pre;
	struct _dlist_node_ * next;
	void * data;
} dlist_node_s;

 struct _dlist_ {
	dlist_node_s * first;
	dlist_node_s * last;
}_dlist;

dlist_node_s * dlist_node_create(void *data)
{
	dlist_node_s  *dlist_node = (dlist_node_s *)malloc(sizeof(dlist_node_s));

	if (dlist_node != NULL) {
		dlist_node->data = data;
		dlist_node->next = NULL;
		dlist_node->pre = NULL;
	}
	return dlist_node;
}
void dlist_node_destory(dlist_node_s *dlist_node)
{
	if (dlist_node != NULL) {
		free(dlist_node);
	}
}
dlist_s * dlist_create(void)
{
	dlist_s * dlist = (dlist_s *)malloc(sizeof(dlist_s));

	if (dlist != NULL) {
		dlist->first = NULL;
		dlist->last = NULL;
	}
	return  dlist;
}
dlist_node_s *dlist_get_node(dlist_s * thiz,size_t index,int fail_return_last)
{
	dlist_node_s *dlist ;
	
	if (thiz == NULL) {
		return NULL;
	}
	if (index == 0) {
		return thiz->first;
	} else if ((index+1) == 0) {
		return thiz->last;
	}
	
	dlist = thiz->first;
	while ((dlist != NULL) && (index > 0)) {
		dlist = dlist->next;
		index--;
	}
	if (dlist == NULL && fail_return_last) {
		dlist = thiz->last;	
	}
	return dlist;
}
size_t dlist_get_length(dlist_s * thiz)
{
	dlist_node_s * node;
	size_t count = 0;
	
	if (thiz == NULL) {
		return 0;
	} 
	node = thiz->first;
	while (node != NULL) {
		node = node->next;
		count++;
	}
	return count;
}
dlist_ret_e dlist_insert(dlist_s *thiz,size_t index,void *data)
{
	dlist_node_s * dlist_node  = dlist_node_create(data);

	if (dlist_node == NULL) {
		return DLIST_RET_MEM;
	}

	if (thiz == NULL) {
		dlist_node_destory(dlist_node);
		return DLIST_RET_MEM;
	}
	if (thiz->first == NULL) {
		thiz->first = dlist_node;
		thiz->last = dlist_node;
	} else {
		dlist_node_s * insert_node = dlist_get_node(thiz,index,1);
		if (index < (dlist_get_length(thiz))) {
			if (thiz->first == insert_node) {
				thiz->first = dlist_node;
			} else {
				insert_node->pre->next = dlist_node;
				dlist_node->pre = insert_node->pre;
			}
			dlist_node->next = insert_node;
			insert_node->pre = dlist_node;
		} else {
			thiz->last = dlist_node;
			insert_node->next = dlist_node;
			dlist_node->pre = insert_node;
		}
	}
	return DLIST_RET_OK;
}
dlist_ret_e  dlist_append(dlist_s *thiz,void *data)
{
	return dlist_insert(thiz,-1,data);
}

dlist_ret_e dlist_preappend(dlist_s * thiz, void *data)
{
	return dlist_insert(thiz,0,data);
}
dlist_ret_e dlist_delete(dlist_s *thiz,size_t index)
{
	dlist_node_s * del_node = dlist_get_node(thiz,index,0);

	if (del_node == NULL) {
		return DLIST_RET_PRA;
	}
	if ((del_node != thiz->first) && (del_node != thiz->last)) {
		del_node->pre->next = del_node->next;
		del_node->next->pre = del_node->pre;
	}
	
	if (del_node == thiz->first) {
		thiz->first = del_node->next;
		if (del_node->next != NULL) {
			del_node->next->pre = NULL;
		}
	}
	
	if (del_node == thiz->last) {
		thiz->last = del_node->pre;
		if (del_node->pre != NULL) {
			del_node->pre->next = NULL;
		}
	}
	
	dlist_node_destory(del_node);

	return DLIST_RET_OK;
}
dlist_ret_e dlist_get_by_index(dlist_s *thiz,size_t index,void **data)
{
	dlist_node_s * node =	dlist_get_node(thiz, index, 0);
	
	if (node != NULL) {
		*data = node->data;
		return DLIST_RET_OK;
	}
	return DLIST_RET_PRA;
}

dlist_ret_e dlist_set_by_index(dlist_s *thiz,size_t index,void *data)
{
	dlist_node_s * node =	dlist_get_node(thiz, index, 0);
	
	if (node != NULL) {
		node->data = data ; 
		return DLIST_RET_OK;
	}
	return DLIST_RET_PRA;
}

dlist_ret_e dlist_destory(dlist_s *thiz)
{
	dlist_node_s * node;
	dlist_node_s * iter;
	
	if (thiz == NULL) {
		return DLIST_RET_PRA;
	}
	node = thiz->first;
	iter = node;
	 while (node != NULL) {
	 	iter = iter->next;
		dlist_node_destory(node);
		node = iter;
	 }
	 free(thiz);
	return DLIST_RET_OK;
}

dlist_ret_e dlist_printf(dlist_s *thiz,printf_f print)
{
	dlist_node_s * node;
	
	if (thiz == NULL) {
		return DLIST_RET_PRA;
	}
	node = thiz->first;

	while (node != NULL) {
		print(node->data);
		node = node->next;
	}
	printf("\n\n");
	return DLIST_RET_OK;
}


dlist_ret_e dlist_loop(dlist_s *thiz,loop_func loop_fun,void **value)
{
	dlist_node_s *node;

	if ((thiz == NULL)) {
		return DLIST_RET_PRA;
	}
	node = thiz->first;
	while (node != NULL) {
		if (loop_fun(node->data,value) == DLIST_RET_END) {
			break;
		}
		node = node->next;
	}
	return DLIST_RET_OK;
}





